﻿/*
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef FFTOOLS_FFMPEG_H
#define FFTOOLS_FFMPEG_H

#include "config.h"

#include <stdint.h>
#include <stdio.h>
#include <signal.h>

#include "cmdutils.h"

#include "libavformat/avformat.h"
#include "libavformat/avio.h"

#include "libavcodec/avcodec.h"

#include "libavfilter/avfilter.h"

#include "libavutil/avutil.h"
#include "libavutil/dict.h"
#include "libavutil/eval.h"
#include "libavutil/fifo.h"
#include "libavutil/hwcontext.h"
#include "libavutil/pixfmt.h"
#include "libavutil/rational.h"
#include "libavutil/thread.h"
#include "libavutil/threadmessage.h"

#include "libswresample/swresample.h"

#define VSYNC_AUTO       -1
#define VSYNC_PASSTHROUGH 0
#define VSYNC_CFR         1
#define VSYNC_VFR         2
#define VSYNC_VSCFR       0xfe
#define VSYNC_DROP        0xff

#define MAX_STREAMS 1024    /* arbitrary sanity check value */

enum HWAccelID {
    HWACCEL_NONE = 0,
    HWACCEL_AUTO,
    HWACCEL_GENERIC,
    HWACCEL_VIDEOTOOLBOX,
    HWACCEL_QSV,
    HWACCEL_CUVID,
};

typedef struct HWAccel {
    const char *name;
    int (*init)(AVCodecContext *s);
    enum HWAccelID id;
    enum AVPixelFormat pix_fmt;
} HWAccel;

typedef struct HWDevice {
    const char *name;
    enum AVHWDeviceType type;
    AVBufferRef *device_ref;
} HWDevice;

/* select an input stream for an output stream */
typedef struct StreamMap {
    int disabled;           /* 1 is this mapping is disabled by a negative map */
    int file_index;
    int stream_index;
    int sync_file_index;
    int sync_stream_index;
    char *linklabel;       /* name of an output link, for mapping lavfi outputs */
} StreamMap;

typedef struct {
    int  file_idx,  stream_idx,  channel_idx; // input
    int ofile_idx, ostream_idx;               // output
} AudioChannelMap;
// 每一路输入或者输出都对应一个OptionsContext，每一路输入或输出也对应一个OptionGroup
typedef struct OptionsContext {
    // 因为要用到默认的一些 codec_opts、format_opts等字段，因为这些字段需要传入 ffmpeg API，是需要传入 AVDictionary
    OptionGroup *g;

    /* input/output options */
    int64_t start_time;     // -ss 相对稳健开头的开始时间
    int64_t start_time_eof; // -sseof 相对于文件末尾的开始时间
    int seek_timestamp;
    const char *format;

    SpecifierOpt *codec_names;
    int        nb_codec_names;
    SpecifierOpt *audio_channels;
    int        nb_audio_channels;
    SpecifierOpt *audio_sample_rate;
    int        nb_audio_sample_rate;
    SpecifierOpt *frame_rates;
    int        nb_frame_rates;
    SpecifierOpt *frame_sizes;
    int        nb_frame_sizes;
    SpecifierOpt *frame_pix_fmts;
    int        nb_frame_pix_fmts;

    /* input options */
    int64_t input_ts_offset;
    int loop;
    int rate_emu;
    int accurate_seek;
    int thread_queue_size;

    SpecifierOpt *ts_scale;
    int        nb_ts_scale;
    SpecifierOpt *dump_attachment;  // -dump_attachment 选项
    int        nb_dump_attachment;
    SpecifierOpt *hwaccels;
    int        nb_hwaccels;
    SpecifierOpt *hwaccel_devices;
    int        nb_hwaccel_devices;
    SpecifierOpt *hwaccel_output_formats;
    int        nb_hwaccel_output_formats;
    SpecifierOpt *autorotate;
    int        nb_autorotate;

    /* output options */
    StreamMap *stream_maps;
    int     nb_stream_maps;
    AudioChannelMap *audio_channel_maps; /* one info entry per -map_channel */
    int           nb_audio_channel_maps; /* number of (valid) -map_channel settings */
    int metadata_global_manual;
    int metadata_streams_manual;
    int metadata_chapters_manual;
    const char **attachments;
    int       nb_attachments;

    int chapters_input_file;

    int64_t recording_time;

    // 录制停止时间
    int64_t stop_time;

    // 限制录制文件大小
    uint64_t limit_filesize;
    float mux_preload;
    float mux_max_delay;

    // 当音视频有一个结束，就会立即结束
    int shortest;

    // 比特精确模式，用此标志时，它强制解码器（或编码器，取决于上下文）的输出必须是完全可重现的。
    // 这意味着在相同的输入数据、相同的编译版本的 FFmpeg 库、相同的 CPU 架构上运行，
    // 无论运行多少次或在何种系统负载下，输出的每一个比特都必须完全相同
    int bitexact;

    // 是否禁止视频
    int video_disable;
    // 是否禁止音频
    int audio_disable;
    // 是否禁止字幕
    int subtitle_disable;
    // 是否禁止data
    int data_disable;

    /* indexed by output file stream index */
    int   *streamid_map;
    int nb_streamid_map;

    SpecifierOpt *metadata;
    int        nb_metadata;
    SpecifierOpt *max_frames;
    int        nb_max_frames;
    SpecifierOpt *bitstream_filters;
    int        nb_bitstream_filters;
    SpecifierOpt *codec_tags;   // 对应-tag 选项 强制编码器标记
    int        nb_codec_tags;
    SpecifierOpt *sample_fmts;
    int        nb_sample_fmts;
    SpecifierOpt *qscale;
    int        nb_qscale;
    SpecifierOpt *forced_key_frames;
    int        nb_forced_key_frames;
    SpecifierOpt *force_fps;
    int        nb_force_fps;
    SpecifierOpt *frame_aspect_ratios;
    int        nb_frame_aspect_ratios;
    SpecifierOpt *rc_overrides;
    int        nb_rc_overrides;
    SpecifierOpt *intra_matrices;
    int        nb_intra_matrices;
    SpecifierOpt *inter_matrices;
    int        nb_inter_matrices;
    SpecifierOpt *chroma_intra_matrices;
    int        nb_chroma_intra_matrices;
    SpecifierOpt *top_field_first;
    int        nb_top_field_first;
    SpecifierOpt *metadata_map;
    int        nb_metadata_map;
    SpecifierOpt *presets;
    int        nb_presets;
    SpecifierOpt *copy_initial_nonkeyframes;
    int        nb_copy_initial_nonkeyframes;
    SpecifierOpt *copy_prior_start;
    int        nb_copy_prior_start;
    SpecifierOpt *filters;
    int        nb_filters;
    SpecifierOpt *filter_scripts;
    int        nb_filter_scripts;
    SpecifierOpt *reinit_filters;
    int        nb_reinit_filters;
    SpecifierOpt *fix_sub_duration;
    int        nb_fix_sub_duration;
    SpecifierOpt *canvas_sizes;
    int        nb_canvas_sizes;
    SpecifierOpt *pass;
    int        nb_pass;
    SpecifierOpt *passlogfiles;
    int        nb_passlogfiles;
    SpecifierOpt *max_muxing_queue_size;
    int        nb_max_muxing_queue_size;
    SpecifierOpt *guess_layout_max;
    int        nb_guess_layout_max;
    SpecifierOpt *apad;
    int        nb_apad;
    SpecifierOpt *discard;
    int        nb_discard;
    SpecifierOpt *disposition;
    int        nb_disposition;
    SpecifierOpt *program;
    int        nb_program;
    SpecifierOpt *time_bases;
    int        nb_time_bases;
    SpecifierOpt *enc_time_bases;
    int        nb_enc_time_bases;
} OptionsContext;


// 一个inputfilter只属于一个graph
typedef struct InputFilter {
    // 滤镜上下文，这是一个入口滤镜，比如 audio  abuffer, video buffer
    AVFilterContext    *filter;

    // 关联的输入流，滤镜中的 AVStream 从哪里来的
    struct InputStream *ist;    // 每个inputfilter对应一个输入流

    // 关联的 FilterGraph
    struct FilterGraph *graph;  // 对应个graph

    // 名称，通过 describe_filter_link
    uint8_t            *name;

    // 音视频数据 codec type
    enum AVMediaType    type;   // AVMEDIA_TYPE_SUBTITLE for sub2video

    // AVFrame 的缓存队列，当输入流解码出来 AVFrame,但是还不能调用 av_buffersrc_add_frame_flags 发往滤镜，就会缓存到 frame_queue
    AVFifoBuffer *frame_queue;      // 未初始化好啊filtergraph时先将frame缓存

    // parameters configured for this input
    // 输入流的 AVFrame 格式，当值不等于 -1 时表示初始化完成
    int format;

    // 视频相关参数
    int width, height;  // 输入宽高
    AVRational sample_aspect_ratio;

    // 音频相关参数
    int sample_rate;
    int channels;
    uint64_t channel_layout;

    // 部分滤镜可以使用硬件加速
    AVBufferRef *hw_frames_ctx;

    int eof;        // 是否结束输入
} InputFilter;

typedef struct OutputFilter {
    // 滤镜上下文，这个字段对应 buffersink 出口滤镜
    AVFilterContext     *filter;

    // 关了的输出流，关联的输出流，从出口滤镜出来 AVPacket 需要丢进编码器
    struct OutputStream *ost;

    // 关联的 FilterGraph
    struct FilterGraph  *graph;
    uint8_t             *name;

    /* temporary storage until stream maps are processed */
    AVFilterInOut       *out_tmp;
    enum AVMediaType     type;

    /* desired output stream properties */
    int width, height;
    AVRational frame_rate;
    int format;
    int sample_rate;
    uint64_t channel_layout;

    // those are only set if no format is specified and the encoder gives us multiple options
    int *formats;
    uint64_t *channel_layouts;
    int *sample_rates;
} OutputFilter;

// 一个FilterGraph对应可以对应多个输入和多个输出
typedef struct FilterGraph {
    // 全局变量 filtergraphs 数组里的索引
    int            index;

    // 命令行选项 -filter_complex 的值，简单滤镜这个字段是空字符
    const char    *graph_desc;

    // 滤镜容器
    AVFilterGraph *graph;
    // 不知什么作用
    int reconfiguration;

    // 入口滤镜数组
    InputFilter   **inputs;
    // 入口滤镜数组长度
    int          nb_inputs;

    // 出口滤镜数组
    OutputFilter **outputs;
    // 出口滤镜数组长度
    int         nb_outputs;
} FilterGraph;

// 一个输入流可以连接到多个input filter
typedef struct InputStream {
    // stream对应在哪个InputFile，比如0则为input_files[0]
    int file_index;

    // AVFormatContext 里面的 AVStream
    AVStream *st;           // 对应的stream

    // 如果是1会丢读到的 AVPacket，刚开始时都是1。例如文件里有多个视频流，会全部重置为1，然后选出质量最好的视频流，把他discard 设置为0
    int discard;             /* true if stream data should be discarded */

    // 命令行选项 -discard 的值，默认是 AVDISCARD_NONE
    int user_set_discard;

    // 大于0代表输入的流需要进行解码操作，有两个值， DECODING_FOR_OST，DECODING_FOR_FILTER
    int decoding_needed;     /* copy时不需要解码non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */
#define DECODING_FOR_OST    1
#define DECODING_FOR_FILTER 2

    // 解码器上下/解码器实例
    AVCodecContext *dec_ctx;

    // 解码器信息
    AVCodec *dec;

    // 从解码器上下文解码出来的 AVFrame
    AVFrame *decoded_frame;

    // 复制一份 decoded_frame 然后再传递给滤镜
    AVFrame *filter_frame; /* 有filter时使用 a ref of decoded_frame, to be sent to filters */

    // 记录流是什么时候开始处理的，主要是给 -re 选项使用，模拟帧率速度，录制视频模拟直播用的
    int64_t       start;     /* 记录流开始的系统起始时间（和流本身没有关系） time when read started */
    /* predicted dts of the next packet read for this stream or (when there are
     * several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */
    // 下一帧解码时间，时间基以 AV_TIME_BASE
    int64_t       next_dts;
    // 最近一次从 AVPacket 读取的 dts
    int64_t       dts;       ///< dts of the last packet read for this stream (in AV_TIME_BASE units)

    // 下一个 AVFrame 的 pts，通过当前的 AVFrame 的 pts 加上 duration 计算得到
    int64_t       next_pts;  ///< synthetic pts for the next decode frame (in AV_TIME_BASE units)
    // 最近一次从解码器里面解码出来的 AVFrame 的 pts，记录这个值主要是给 send_filter_eof() 用的，防止回滚
    int64_t       pts;       ///< current pts of the decoded frame  (in AV_TIME_BASE units)

    // 在一些流格式，例如 TS 使用，具体还未分析？
    int           wrap_correction_done;

    // 专门给音视频转换时间基用的，因为音频连续性太强，如果有些许误差需要保存下来，在下次转化的时候进行补偿
    int64_t filter_in_rescale_delta_last;

    // 从 av_read_frame() 读取出来的 AVPacket 的最小 pts，不一定是第一帧的 pts，因为可以通过 -ss 选项指定从哪里开始
    int64_t min_pts; /* pts with the smallest value in a current stream */

    // 从 av_read_frame() 读取出来的 AVPacket 的最大 pts, 不一定是最后一帧pts, 因为可以通过 -t 选项
    int64_t max_pts; /* pts with the higher value in a current stream */

    // when forcing constant input framerate through -r,
    // this contains the pts that will be given to the next decoded frame
    // 给 -r 选项使用
    int64_t cfr_next_pts;

    // 记录最近一次解码出来的 AVFrame 里面的 nb_samples
    int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */

    // 默认值是1.0，可通过 -itsscale 记性改变，主要作用是将 pts，dts 放大，放大方法是乘以 ts_scale
    double ts_scale;        // 默认为1.0 通常都是1.0，对应-itsscale 选项

    // 标记是不是已经读取到属于该流的第一个 AVPacket
    int saw_first_ts;

    // 从 OptionsContext 里面转移过来的解码器参数
    AVDictionary *decoder_opts;

    // 指定帧率，选项 -r 使用
    AVRational framerate;    /* framerate forced with -r 在输入强制设置帧率时使用 */

    // 选项 -top 的值，不知道具体作用
    int top_field_first;

    // 选项 -guess_layout_max 的值，猜测最大声道布局
    int guess_layout_max;

    // 命令选项 -autorate 的值，是否插入纠正旋转的 filter 滤镜
    int autorotate; // 默认1，对应-autorotate 选项，自动插入正确的旋转过滤器

    // 不知？
    int dr1;

    /* decoded data from this stream goes into all those filters, 可以接入多个filter
     * currently video and audio only */

    // 输入流绑定的入口滤镜，可以是绑定多个入口滤镜的
    InputFilter **filters;

    // 入口滤镜个数
    int        nb_filters;

    // 选项 -reinit_filter 的值，0代表输入信息如果产生变化也不重新初始化 FilterGraph，也就是值 AVFrame 宽高中途有变化
    int reinit_filters;

    /* 关于硬编码相关的信息，暂时不分析 */
    /* hwaccel options */
    enum HWAccelID hwaccel_id;
    enum AVHWDeviceType hwaccel_device_type;
    char  *hwaccel_device;
    enum AVPixelFormat hwaccel_output_format;

    /* hwaccel context */
    void  *hwaccel_ctx;
    void (*hwaccel_uninit)(AVCodecContext *s);
    int  (*hwaccel_get_buffer)(AVCodecContext *s, AVFrame *frame, int flags);
    int  (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
    enum AVPixelFormat hwaccel_pix_fmt;
    enum AVPixelFormat hwaccel_retrieved_pix_fmt;
    AVBufferRef *hw_frames_ctx;

    /* stats */
    // combined size of all the packets read
    // 统计当前流一共读出字节数据
    uint64_t data_size;
    /* number of packets successfully read for this stream */
    // 从 av_read_frame() 读取 AVPacket 数量
    uint64_t nb_packets;
    // number of frames/samples retrieved from the decoder
    // 从解码器解码出来的 AVFrame 数量
    uint64_t frames_decoded;

    // 统计 nb_samples 总采样个数
    uint64_t samples_decoded;

    // 不再使用的垃圾代码
    int64_t *dts_buffer;
    int nb_dts_buffer;

    // 从解码器已经解除一帧 AVFrame，这个字段就会置1，记录下开始解码了
    int got_output;
} InputStream;

typedef struct InputFile {
    // 容器上下文，也叫容器实例。
    AVFormatContext *ctx;

    // 代表输入文件是否已经读取结束，不一定读取到文件末尾，也可以指定 -t 选项指定输入文件的时长。
    int eof_reached;      /* true if eof reached */

    // 代表上一次 av_read_frame() 函数是否返回 EAGAIN 错误码，
    int eagain;           /* true if last read attempt returned EAGAIN */

    // 输入文件的第一个流在 input_streams[] 数组的索引位置
    int ist_index;        /* 对应input_streams的位置，每个输入文件的stream是紧靠排列
                                index of first stream in input_streams */

    // 输入文件应该被循环读取多少次
    int loop;             /* set number of times input stream should be looped */

    // 在循环时使用，开始第二次循环之前，会把 duration 设置为流的时长，第二次循环的时候 pts + duration 这样时间就拼在一起了
    int64_t duration;     /* actual duration of the longest stream in a file
                             at the moment when looping happens */

    // duration 的时间基
    AVRational time_base; /* time base of the duration  文件长度的 time_base*/

    // 命令行选项 -itsoffset 5 赋值，作用暂不知？
    int64_t input_ts_offset;

    //
    int64_t ts_offset;

    // 记录最近一次读出来的 AVPacket dts 时间
    int64_t last_ts;

    // 命令行指定 -ss 选项设置的参数，时间基 AV_TIME_BASE
    int64_t start_time;   /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */

    // 暂未用到，不知具体用法
    int seek_timestamp;

    // 输入文件的最大处理时长，例如一个文件3分钟，可以通过命令行指定 -t 60 指定只处理1分钟时长
    int64_t recording_time;

    // 输入文件实际的流长度
    int nb_streams;       /* 文件其stream数量 number of stream that ffmpeg is aware of; may be different
                             from ctx.nb_streams if new streams appear during av_read_frame() */

    // 动态读取的流数量
    int nb_streams_warn;  /* number of streams that the user was warned of */

    // 命令行指定 -re 赋值过来，代表按照帧率去输出
    int rate_emu;
    int accurate_seek;

#if HAVE_THREADS
    AVThreadMessageQueue *in_thread_queue;
    pthread_t thread;           /* thread reading from this file */
    int non_blocking;           /* reading packets from the thread should not block */
    int joined;                 /* the thread has been joined */
    int thread_queue_size;      /* maximum number of queued packets */
#endif
} InputFile;

enum forced_keyframes_const {
    FKF_N,
    FKF_N_FORCED,
    FKF_PREV_FORCED_N,
    FKF_PREV_FORCED_T,
    FKF_T,
    FKF_NB
};

#define ABORT_ON_FLAG_EMPTY_OUTPUT (1 <<  0)

extern const char *const forced_keyframes_const_names[];

typedef enum {
    ENCODER_FINISHED = 1,
    MUXER_FINISHED = 2,
} OSTFinished ;

typedef struct OutputStream {
    // 全局变量 output_files
    int file_index;          /*对应的output_files序号  file index */

    // 索引是容器里最大的那个流的索引
    int index;               /* stream index in the output file */

    // 输出流对应的输入流，-1 代表没有对应的输入流，通常是因为使用了滤镜，把多个输入流混合成一个输出流
    int source_index;        /* 对应input steam的序号 InputStream index */

    // 从容器上下文里面复制而来
    AVStream *st;            /* stream in the output file */

    // 输出数据需不需要进行编码操作，只要在命令行参数没使用 copy 选项，都是需要进行编码操作的
    int encoding_needed;     /* true if encoding needed for this stream */

    // 统计一共往编码器输入了多少个 AVFrame
    int frame_number;
    /* input pts and corresponding output pts
       for A/V sync */
    // 不知道作用?
    struct InputStream *sync_ist; /* input stream to sync against */

    /* output frame counter, could be changed to some true timestamp */ // FIXME look at frame_number
    // 等同于 frame_number
    int64_t sync_opts;

    /* pts of the first frame encoded for this stream, used for limiting
     * recording time */
    // 流的第一个 AVFrame 的 pts，可能是音频的 pts，也可能是视频的 pts。主要用来限制输出时长的，配合 recording_time
    int64_t first_pts;

    /* dts of the last packet sent to the muxer */
    // 最近一次写入容器文件的 AVPacket 的 dts
    int64_t last_mux_dts;

    // the timebase of the packets sent to the muxer
    // 复用器的时间基，也就是 AVPacket 写入文件的时候，他的 pts 需要转换成 mux_timebase 的时间基
    AVRational mux_timebase;

    // 编码的时间基，AVFrame 发送给编码器之前，AVFrame 的 pts 需要转换成 enc_timebase 的时间基
    AVRational enc_timebase;

    // 字节流滤镜实例，可以用命令行参数 -bsf 指定，例如 -bsf:vh264_mp4toannexb 将 H.264 转 H.264AnnexB 标准（用于实时流）
    AVBSFContext            **bsf_ctx;
    int                    nb_bitstream_filters;

    // 编码器上下文
    AVCodecContext *enc_ctx;

    // 编码器参数，主要用于 stream copy 场景，将输入流的编码参数赋值给输出流
    AVCodecParameters *ref_par; /* associated input codec parameters with encoders options applied */

    // 编码器信息
    AVCodec *enc;

    // 选项 -frames 的值，限制此输出流最多输出多少个 AVFrame
    int64_t max_frames;

    // 从 FilterGraph 出口滤镜里面读出来的 AVFrame
    AVFrame *filtered_frame;

    // 指向上一次发送给编码器的 AVFrame
    AVFrame *last_frame;

    // 没搞懂
    int last_dropped;
    int last_nb0_frames[3];

    // 硬编码器上下文
    void  *hwaccel_ctx;

    /* video only */
    // 帧率，通过 -r 选项指定
    AVRational frame_rate;
    // 此输出流是否是常量帧率
    int is_cfr;
    // 强制指定帧率， 通过 -force_fps 选项指定，是个布尔值
    int force_fps;

    // 隔行扫描视频使用
    int top_field_first;

    // 没搞懂
    int rotate_overridden;
    // 没搞懂
    double rotate_override_value;

    // far 视频宽高比，用这个比例拉伸 width 和 height 作为显示窗口，图像播放不会扭曲
    AVRational frame_aspect_ratio;

    /* forced key frames */
    int64_t forced_kf_ref_pts;
    int64_t *forced_kf_pts;
    int forced_kf_count;
    int forced_kf_index;
    char *forced_keyframes;
    AVExpr *forced_keyframes_pexpr;
    double forced_keyframes_expr_const_values[FKF_NB];

    /* audio only */
    // 命令行 map 选项使用
    int *audio_channels_map;             /* list of the channels id to pick from the source stream */
    int audio_channels_mapped;           /* number of channels in audio_channels_map */

    // 日志
    char *logfile_prefix;
    FILE *logfile;

    // 输出流关联的出口滤镜，输出流需要从这个出口滤镜读取 AVFrame
    OutputFilter *filter;       // 最后一级输出过滤器的参数，要匹配输出文件，比如视频时的宽高
    char *avfilter;

    // 通过 -filter 指定滤镜字符串
    char *filters;         ///< filtergraph associated to the -filter option
    // 通过 -filter_script 滤镜脚本文件的名称以及路径
    char *filters_script;  ///< filtergraph script associated to the -filter_script option

    // 编码器参数
    AVDictionary *encoder_opts;
    // 视频重采样参数
    AVDictionary *sws_dict;
    // 音频重采样参数
    AVDictionary *swr_opts;
    // 音频重采样参数
    AVDictionary *resample_opts;

    // 选项 -apad 指定，不懂作用
    char *apad;

    // 是否写完了？
    OSTFinished finished;        /* no more packets should be written for this stream */
    // 输出流不可用
    int unavailable;                     /* true if the steram is unavailable (possibly temporarily) */

    // 选项 -copy 指定
    int stream_copy;

    // init_output_stream() has been called for this stream
    // The encoder and the bitstream filters have been initialized and the stream
    // parameters are set in the AVStream.
    // 当前流是否初始化，初始化了才能往容器里写头部信息
    int initialized;

    // 代表没有找到输出流对应的输入流
    int inputs_done;

    // 选项 -attach 指定，添加附件用的
    const char *attachment_filename;

    // 选项 -copyinkf 指定
    int copy_initial_nonkeyframes;

    // 选项 -copypriorss 指定
    int copy_prior_start;

    // 选项 -disposition 指定
    char *disposition;

    // 指定了 -pix_fmt 时改值置为1
    int keep_pix_fmt;

    /* stats */
    // combined size of all the packets written
    // 该流所有 AVPacket 的 data_size 字段累计而来
    uint64_t data_size;

    // number of packets send to the muxer
    // 已经有多少 AVPacket 送进了复用器
    uint64_t packets_written;

    // number of frames/samples sent to the encoder
    // 有多少 AVFrame 被送进了编码器
    uint64_t frames_encoded;

    // 有多少音频采样样本被送进了编码器
    uint64_t samples_encoded;

    /* packet quality factor */
    // 视频编码器的编码质量
    int quality;

    // 选项 -max_muxing_queue_size 指定，默认128，作用就是所有流都准备好了才能写入头部信息，没准备好之前的的AVPacket放入muxing_queue队列
    int max_muxing_queue_size;

    /* the packets are buffered here until the muxer is ready to be initialized */
    AVFifoBuffer *muxing_queue;

    /* packet picture type */
    // 最近一次编码器输出的 AVPacket 图片类型，例如I帧、P帧、B帧
    int pict_type;

    /* frame encode sum of squared error values */
    // 统计编码错误
    int64_t error[4];
} OutputStream;

typedef struct OutputFile {
    // 输出容器上下文
    AVFormatContext *ctx;

    // 容器格式的参数，是从 OptiosContext 里面的 OptionGroup 的 format_opts 赋值过来的
    AVDictionary *opts;

    // 输出文件的第一个流在 out_streams 数组里面的索引，output_streams 数组是一个全局变量，里面包含所有输出文件的输出流
    int ost_index;       /* 对应输出文件的stream index of the first stream in output_streams */

    // 选项 -t 设置输出文件的时长，单位是微妙
    int64_t recording_time;  ///< desired length of the resulting file in microseconds == AV_TIME_BASE units

    // 标记输出文件的起始时间，例如一个输入文件本来是 6分钟，用 -ss 120 指定 start_time，这样，输出文件就会裁剪成 2~6分钟的视频。丢弃前面2分钟
    int64_t start_time;      ///< start time in microseconds == AV_TIME_BASE units

    // 选项 -fs 限制输出文件大小，达到这个大小立即退出
    uint64_t limit_filesize; /* filesize limit expressed in bytes */

    // 选项 -shorttest，当最短的输出流结束的时候，整个输出流也一并结束
    int shortest;

    int header_written;
} OutputFile;

// 文件和流是分别保存的
extern InputStream **input_streams; // 输入流的数组 input_streams和input_files 不一样
extern int        nb_input_streams; // 输入流的个数
extern InputFile   **input_files;   // 输入文件（也可能是设备）的数组
extern int        nb_input_files;   // 输入文件的个数
// 同上的意思
extern OutputStream **output_streams;   // 输出流的数组
extern int         nb_output_streams;   // 输出流的总个数
extern OutputFile   **output_files;     // 输出文件的数组
extern int         nb_output_files;     // 输出文件的个数

extern FilterGraph **filtergraphs;  // filter相关
extern int        nb_filtergraphs;  // 个数

extern char *vstats_filename;
extern char *sdp_filename;

extern float audio_drift_threshold;
extern float dts_delta_threshold;
extern float dts_error_threshold;

extern int audio_volume;        // 音频音量
extern int audio_sync_method;
extern int video_sync_method;
extern float frame_drop_threshold;
extern int do_benchmark;
extern int do_benchmark_all;
extern int do_deinterlace;
extern int do_hex_dump;
extern int do_pkt_dump;
extern int copy_ts;
extern int start_at_zero;
extern int copy_tb;
extern int debug_ts;
extern int exit_on_error;
extern int abort_on_flags;
extern int print_stats;
extern int qp_hist;
extern int stdin_interaction;
extern int frame_bits_per_raw_sample;
extern AVIOContext *progress_avio;
extern float max_error_rate;
extern char *videotoolbox_pixfmt;

extern int filter_nbthreads;
extern int filter_complex_nbthreads;
extern int vstats_version;

extern const AVIOInterruptCB int_cb;
/*
 * options，定义了所有的参数、参数类型、参数是否有值、是放到全局变量中
 * 还是放到OptionsContext结构的某个变量中，或者是调用一个函数来处理参数的值。
*/
extern const OptionDef options[];
extern const HWAccel hwaccels[];
extern AVBufferRef *hw_device_ctx;
#if CONFIG_QSV
extern char *qsv_device;
#endif
extern HWDevice *filter_hw_device;


void term_init(void);
void term_exit(void);

void reset_options(OptionsContext *o, int is_input);
void show_usage(void);

void opt_output_file(void *optctx, const char *filename);

void print_avoptions(AVDictionary *m);
void remove_avoptions(AVDictionary **a, AVDictionary *b);
void assert_avoptions(AVDictionary *m);

int guess_input_channel_layout(InputStream *ist);

enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *avctx, AVCodec *codec, enum AVPixelFormat target);
void choose_sample_fmt(AVStream *st, AVCodec *codec);

int configure_filtergraph(FilterGraph *fg);
int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out);
void check_filter_outputs(void);
int ist_in_filtergraph(FilterGraph *fg, InputStream *ist);
int filtergraph_is_simple(FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
int init_complex_filtergraph(FilterGraph *fg);

void sub2video_update(InputStream *ist, AVSubtitle *sub);

int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);

int ffmpeg_parse_options(int argc, char **argv);

int videotoolbox_init(AVCodecContext *s);
int qsv_init(AVCodecContext *s);
int cuvid_init(AVCodecContext *s);

HWDevice *hw_device_get_by_name(const char *name);
int hw_device_init_from_string(const char *arg, HWDevice **dev);
void hw_device_free_all(void);

int hw_device_setup_for_decode(InputStream *ist);
int hw_device_setup_for_encode(OutputStream *ost);

int hwaccel_decode_init(AVCodecContext *avctx);

#endif /* FFTOOLS_FFMPEG_H */
